#include <QBitmap>
#include <cstdlib>
#include <ctime>
#include "TerrainFactory.hpp"
#include "Values.hpp"
#include "Vec.hpp"
#include "ColorIdGenerator.hpp"

Terrain*        TerrainFactory::createNewTerrain(std::string const& fileName)
{
    QPixmap     pixmap;

    if (pixmap.load(fileName.data()) == true)
    {
        QImage              image = pixmap.toImage();
        uchar               *pixels = image.bits(), *ptr;
        unsigned int        width = image.width(), height = image.height();
        unsigned int        tmp, bytesPerPixel = image.depth() / 8, bytesPerLine = image.bytesPerLine();
        ColorIdGenerator    generator;

        DrawableObject*		terrainDrawable = new DrawableObject();
        Terrain*            terrain = new Terrain(width, height, terrainDrawable);
        unsigned int		nbVertices = width * height, nbIndexes = 0;
        VertexAttributes*	terrainVertices = new VertexAttributes[nbVertices];
        unsigned int*		terrainIndexes = generateIndexes(width, height, nbIndexes);

        float               minX = width / 2.f, minY = height / 2.f;

        for (unsigned int itH = 0; itH < width; itH++)
        {
            for (unsigned int itW = 0; itW < height; itW++)
            {
                tmp = itH * width + itW;
                ptr = pixels + itH * bytesPerLine + itW * bytesPerPixel;
                terrainVertices[tmp].posX = static_cast<float>(itW) - minX;
                terrainVertices[tmp].posY = static_cast<float>(ptr[0] / 10.0);
                terrainVertices[tmp].posZ = static_cast<float>(itH) - minY;

                terrainVertices[tmp].colorR = 1.f;
                terrainVertices[tmp].colorG = 1.f;
                terrainVertices[tmp].colorB = 1.f;

                terrainVertices[tmp].textX = static_cast<float>(itW) / static_cast<float>(width);
                terrainVertices[tmp].textY = static_cast<float>(itH) / static_cast<float>(height);
                terrainVertices[tmp].textZ = 0;

                generator.generate();
                terrainVertices[tmp].idX = generator.getR();
                terrainVertices[tmp].idY = generator.getG();
                terrainVertices[tmp].idZ = generator.getB();

            }
        }
        terrainDrawable->setVertices(nbVertices, terrainVertices);
        terrainDrawable->setIndex(nbIndexes, terrainIndexes);
        calculateNormals(terrainDrawable, width, height);

        return terrain;
    }

    return NULL;
}

Terrain*        TerrainFactory::createNewTerrain(const unsigned int width, const unsigned int height, const unsigned int maxHeight)
{
	srand(static_cast<unsigned int>(time(0)));

    DrawableObject*		terrainDrawable = new DrawableObject();
    Terrain*            terrain = new Terrain(width, height, terrainDrawable);
    unsigned int		nbVertices = width * height, nbIndexes = 0;
    VertexAttributes*	terrainVertices = new VertexAttributes[nbVertices];
    unsigned int*		terrainIndexes = generateIndexes(width, height, nbIndexes);

    float               minX = width / 2, minY = height / 2;
	unsigned int		tmp;
    ColorIdGenerator    generator;

	for (unsigned int itH = 0; itH < height; itH++)
	{
		for (unsigned int itW = 0; itW < width; itW++)
		{
			tmp = itH * width + itW;
            terrainVertices[tmp].posX = static_cast<float>(itW) - minX;
            terrainVertices[tmp].posY = (maxHeight == 0) ? 0.f : static_cast<float>(rand() % maxHeight);
            terrainVertices[tmp].posZ = static_cast<float>(itH) - minY;

            terrainVertices[tmp].colorR = 1.f;
            terrainVertices[tmp].colorG = 1.f;
            terrainVertices[tmp].colorB = 1.f;

            terrainVertices[tmp].textX = static_cast<float>(itW) / static_cast<float>(width);
            terrainVertices[tmp].textY = static_cast<float>(itH) / static_cast<float>(height);
            terrainVertices[tmp].textZ = 0;

            generator.generate();
            terrainVertices[tmp].idX = generator.getR();
            terrainVertices[tmp].idY = generator.getG();
            terrainVertices[tmp].idZ = generator.getB();

		}
	}

    terrainDrawable->setVertices(nbVertices, terrainVertices);
    terrainDrawable->setIndex(nbIndexes, terrainIndexes);
    calculateNormals(terrainDrawable, width, height);

    return terrain;
}

void					TerrainFactory::calculateNormals(DrawableObject* object, const unsigned int width, const unsigned int height)
{
	VertexAttributes*	vertex = object->getVertices();
	unsigned int		current;
	unsigned int		right;
	unsigned int		bottom;

	for (unsigned int itH = 0; itH < height - 1; itH++)
	{
		for (unsigned int itW = 0; itW < width - 1; itW++)
		{
			current = itH * width + itW;
			right = itH * width + itW + 1;
			bottom = (itH + 1) * width + itW;

            Vec3f   v1(vertex[bottom].posX - vertex[current].posX, vertex[bottom].posY - vertex[current].posY, vertex[bottom].posZ - vertex[current].posZ);
            Vec3f   v2(vertex[right].posX - vertex[current].posX, vertex[right].posY - vertex[current].posY, vertex[right].posZ - vertex[current].posZ);

			Vec3f	normal = v2.prodVec(v1);

			vertex[current].normX = normal[0];
			vertex[current].normY = normal[1];
			vertex[current].normZ = normal[2];
		}
	}
}

unsigned int*		TerrainFactory::generateIndexes(const unsigned int nbVertexWidth, const unsigned int nbVertexHeight, unsigned int& vertexesSize)
{
	unsigned int*	mapIndexes = new unsigned int[nbVertexWidth * nbVertexHeight * 6];
	unsigned int	itIndex = 0;

	for (unsigned int itH = 0; itH < nbVertexHeight - 1; itH++)
	{
		for (unsigned int itW = 0; itW < nbVertexWidth - 1; itW++)
		{
			mapIndexes[itIndex] = itH * nbVertexHeight + itW;
			itIndex++;
			mapIndexes[itIndex] = itH * nbVertexHeight + (itW + 1);
			itIndex++;
			mapIndexes[itIndex] = (itH + 1) * nbVertexHeight + itW;
			itIndex++;

			mapIndexes[itIndex] = itH * nbVertexHeight + (itW + 1);
			itIndex++;
			mapIndexes[itIndex] = (itH + 1) * nbVertexHeight + (itW + 1);
			itIndex++;
			mapIndexes[itIndex] = (itH + 1) * nbVertexHeight + (itW);
			itIndex++;
		}
	}

	vertexesSize = itIndex;

	return mapIndexes;
}
